package com.gyt.manager.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import com.gyt.manager.common.base.response.Page;
import com.gyt.manager.common.component.CosComponent;
import com.gyt.manager.common.component.LogComponent;
import com.gyt.manager.common.component.RedisComponent;
import com.gyt.manager.common.constant.DefinedConstant;
import com.gyt.manager.common.utils.ExcelUtil;
import com.gyt.manager.common.utils.HttpBaseUtil;
import com.gyt.manager.dao.risk.DalMetaLabelInfoMapper;
import com.gyt.manager.dao.risk.LabelInfoMapper;
import com.gyt.manager.domain.result.ForeCastsResult;
import com.gyt.manager.domain.result.WeatherResult;
import com.gyt.manager.domain.result.mode.City;
import com.gyt.manager.domain.risk.DalMetaLabelInfo;
import com.gyt.manager.domain.risk.LabelInfo;
import com.gyt.manager.service.TestService;
import com.qcloud.cos.model.COSObjectInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.assertj.core.util.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * description.
 *
 * @author gyt create on 2021/5/14 9:38
 */
@Service
public class TestServiceImpl implements TestService {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestServiceImpl.class);

    private static final String CITIES_CODE = "/config/template/citycode_20210406.xlsx";
    private static final String COMMA = ",";
    @Autowired
    @Qualifier("taskExecutor.label.collect")
    private ThreadPoolTaskExecutor executor;
    @Autowired
    private RedisComponent redisComponent;
    @Autowired
    private LogComponent logComponent;
    @Autowired
    private LabelInfoMapper labelInfoMapper;
    @Autowired
    private DalMetaLabelInfoMapper dalMetaLabelInfoMapper;
    @Autowired
    private CosComponent cosComponent;

    @Override
    public String findLogInfo(int type, int lineNum, String key){
        String keyword = "'" + key + "'";
        String filePath = " /home/gyt/func/log/gyt-client/gyt_log/stdout.log";
        String filterPrefix = "tail -";
        String filterSuffix = "|grep ";
        String cmd = filterPrefix + lineNum + filePath;
        if (type == 2){
            cmd = cmd + filterSuffix + keyword;
        }
        return logComponent.executeSuccess(cmd);
    }

    @Override
    public void export(HttpServletResponse response) {
        try {
            InputStream input = cosComponent.downloadFile();
            String fileName = "test.py";
//            fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader(
                    "Content-Disposition",
                    "attachment; filename=\""
                            + fileName + "\"; filename*=utf-8''" + fileName);
            ServletOutputStream out = response.getOutputStream();
//            IOUtils.copy(input, out);
            BufferedInputStream bis = new BufferedInputStream(input);
            byte[] buff = new byte[input.available()];
            int read = bis.read(buff);
            while (read != -1) {
                out.write(buff, 0, buff.length);
                out.flush();
                read = bis.read(buff);
            }
            out.close();
            bis.close();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public Long testExecutor(int type) {
        Long costTime = 0L;
        final int one = 1;
        final int two = 2;
        if (one == type) {
            costTime = noExecutorToGetLabelInfo();
        } else if (two == type) {
            costTime = useExecutorToGetLabelInfo();
        }
        return costTime;
    }

    @Override
    public void findImage(int type, HttpServletResponse response) {
        Resource resource;
        if (type == 1){
            resource = new ClassPathResource("/config/image/emailFooter.jpg");
        } else {
            resource = new ClassPathResource("/config/image/emailHeader.jpg");
        }
        InputStream in = null;
        ServletOutputStream out = null;
        try {
            in = resource.getInputStream();

            String newFileName = "xx.jpg";
            response.setContentType("images/jpeg");
            response.addHeader("Content-Disposition", "attachment; filename=\""
                    + new String(newFileName.getBytes("UTF-8"),"ISO8859-1") + "\"");
            out = response.getOutputStream();
            byte[] buffer = new byte[in.available()];
            while (in.read(buffer) != -1) {
                out.write(buffer);
            }
            out.flush();
        } catch (IOException e) {
            LOGGER.error("error", e);
        } finally {
            try {
                in.close();
                out.close();
            } catch (Exception e) {
                LOGGER.error("closed stream error", e);
            }
        }


    }

    private Long useExecutorToGetLabelInfo() {
        long start = System.currentTimeMillis();
        AtomicInteger count = new AtomicInteger(0);
        final int[] markCnt = {0};
        List<LabelInfo> list = Lists.newArrayList();
        try {
            List<String> labelIds;
            int pageSize = 500, pageNum = 1;
            List<Future<?>> futures = new ArrayList<>();
            do {
                Page.startPage(pageNum++, pageSize);
                labelIds = labelInfoMapper.selectAllLabelId();
                List<String> finalLabelIds = labelIds;
                int finalPageNum = pageNum;
                Future<?> future = executor.submit(() -> {
                    if (!CollectionUtils.isEmpty(finalLabelIds)) {
                        for (String labelId : finalLabelIds) {
                            try {
                                Thread.sleep((long) (Math.random() * 20));
                            } catch (InterruptedException e) {
                                LOGGER.error("sleep failed,thread interrupted", e);
                            }
                            getLabelInfos(list, labelId);
                        }
                        count.addAndGet(list.size());
                        markCnt[0] += list.size();
                        LOGGER.info("current page done! pageNum={},cnt={}",
                                finalPageNum, list.size());
                    }
                });
                futures.add(future);

            } while (labelIds.size() >= pageSize);
            LOGGER.info("executor end, count=[{}], cost:[{}]ms",
                    count, System.currentTimeMillis() - start);
        } catch (Exception e) {
            LOGGER.error("executor error", e);
            throw e;
        } finally {
            long end = System.currentTimeMillis();
            LOGGER.info("executor end, markCnt=[{}], cost:[{}]ms",
                    markCnt[0], end - start);
            return end - start;
        }
    }

    private void getLabelInfos(List<LabelInfo> list, String labelId) {
        DalMetaLabelInfo metaLabelInfo = dalMetaLabelInfoMapper.selectByLabelId(labelId);
        if (null != metaLabelInfo) {
            LabelInfo info = new LabelInfo();
            info.setFlabelId(labelId);
            info.setFlabelName(metaLabelInfo.getFlabelName());
            info.setFlabelLevel(metaLabelInfo.getFlabelLevel());
            list.add(info);
        }
    }

    private Long noExecutorToGetLabelInfo() {
        long start = System.currentTimeMillis();
        List<LabelInfo> list = Lists.newArrayList();
        List<String> labelIds = labelInfoMapper.selectAllLabelId();
        if (!CollectionUtils.isEmpty(labelIds)) {
            for (String labelId : labelIds) {
                getLabelInfos(list, labelId);
            }
        }
        long end = System.currentTimeMillis();
        LOGGER.info("no executor cost[{}]ms", end - start);
        return end - start;
    }

    @Override
    public List<String> importByExcel(InputStream inputStream) {
        List<String> list = Lists.newArrayList();
        try {
            Workbook workbook = WorkbookFactory.create(inputStream);
            Sheet sheet = workbook.getSheetAt(0);
            for (int i = 1; i < 10; i++) {
                Row row = sheet.getRow(i);
                Cell cell = row.getCell(0);
                String cellValue = ExcelUtil.getCellValue(cell);
                list.add(cellValue);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        LOGGER.info("importByExcel.list:{}", JSON.toJSONString(list));
        return list;
    }

    @Override
    public ResponseEntity<byte[]> exportLabelToCsv() {
        Map<String, Object> param = Maps.newHashMap();
        param.put("limitNum", 100);
        List<LabelInfo> labelInfos = labelInfoMapper.findLabelInfos(param);
        final String columnStr = "序号,标签ID,标签名,标签等级";
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(baos, "UTF-8");
            BufferedWriter writer = new BufferedWriter(outputStreamWriter);
            // 写入文件头部
            writer.write(new String(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}));
            writer.write(columnStr);
            writer.newLine();
            int index = 1;
            for (LabelInfo info : labelInfos) {
                StringBuilder builder = new StringBuilder();
                String row = builder.append(index++).append(COMMA).
                        append(info.getFlabelId()).append(COMMA).
                        append(info.getFlabelName()).append(COMMA).
                        append(info.getFlabelLevel()).toString();
                writer.write(row);
                writer.newLine();
            }
            writer.flush();
            HttpHeaders headers = new HttpHeaders();
            String fileName = "label_" + System.currentTimeMillis() + ".csv";
            headers.setContentDispositionFormData("attachment", fileName);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            ResponseEntity responseEntity = new ResponseEntity(baos.toByteArray(), headers, HttpStatus.OK);
            writer.close();
            return responseEntity;
        } catch (Throwable e) {
            LOGGER.error("export csv file failed", e);
        }
        return null;
    }

    @Override
    public Object getWeather(String isTimely, String cities) {
        String[] split = cities.split(DefinedConstant.SPLIT_COMMA);
        return getWeatherInfo(isTimely, split);
    }

    @Override
    public List<City> getCitiesByExcel() {
        List<City> cities = Lists.newArrayList();
        List<String> codes = Lists.newArrayList();
        try {
            Resource resource = new ClassPathResource(CITIES_CODE);
            InputStream inputStream = resource.getInputStream();
            Workbook sheets = WorkbookFactory.create(inputStream);
            if (sheets == null) {
                return cities;
            }
            Sheet sheet = sheets.getSheetAt(0);
            for (int i = 0; i < sheet.getLastRowNum(); i++) {
                if (i == 0) {
                    continue;
                }
                Row row = sheet.getRow(i);
                City city = new City();
                String cityName = ExcelUtil.getCellValue(row.getCell(0));
                String adCode = ExcelUtil.getCellValue(row.getCell(1));
                String cityCode = ExcelUtil.getCellValue(row.getCell(2));
                city.setCityName(cityName);
                city.setAdCode(adCode);
                city.setCityCode(cityCode);
                if (StringUtils.isNotBlank(cityCode)) {
                    codes.add(adCode);
                }
                cities.add(city);
            }
        } catch (Exception e) {
            LOGGER.error(" parse excel error", e);
        }
        LOGGER.info("codes:{}", JSON.toJSONString(codes));
        return cities;
    }

    private ForeCastsResult getForeCastsInfo(String result) {
        JSONObject obj = JSON.parseObject(result);
        if ("1".equals(obj.get("status")) &&
                "OK".equals(obj.getString("info"))) {
            String forecasts = obj.getString("forecasts");
            if (StringUtils.isNotBlank(forecasts)) {
                List<ForeCastsResult> foreCastsResults = JSON.parseArray(forecasts, ForeCastsResult.class);
                return foreCastsResults.get(0);
            }
        }
        return null;
    }

    private WeatherResult getWeatherInfo(String result) {
        JSONObject obj = JSON.parseObject(result);
        if ("1".equals(obj.get("status")) &&
                "OK".equals(obj.getString("info"))) {
            String lives = obj.getString("lives");
            if (StringUtils.isNotBlank(lives)) {
                List<WeatherResult> weatherResults = JSON.parseArray(lives, WeatherResult.class);
                return weatherResults.get(0);
            }
        }
        return null;
    }

    private Object getWeatherInfo(String isTimely, String[] split) {
        List<ForeCastsResult> foreCastsResults = Lists.newArrayList();
        List<WeatherResult> weatherResults = Lists.newArrayList();
        Map<String, String> param = new HashMap<>(8);
        param.put("key", DefinedConstant.weather_Key);
        param.put("extensions", isTimely);//base&all
        int length = split.length;
        for (int i = 0; i < length; i++) {
            String city = split[i];
            param.put("city", city);
            String result = HttpBaseUtil.get(DefinedConstant.weather_url, param);
            if ("all".equals(isTimely)) {
                ForeCastsResult foreCastsInfo = getForeCastsInfo(result);
                if (foreCastsInfo != null) {
                    foreCastsResults.add(foreCastsInfo);
                }
            } else if ("base".equals(isTimely)) {
                WeatherResult weatherInfo = getWeatherInfo(result);
                if (weatherInfo != null) {
                    weatherResults.add(weatherInfo);
                }
            }
        }

        if ("all".equals(isTimely)) {
            return foreCastsResults;
        } else if ("base".equals(isTimely)) {
            return weatherResults;
        }
        return null;
    }

    @Scheduled(cron = "0 10 17,20,22,23 * * ? ")
    public List<ForeCastsResult> getAllWeatherInfo() {
        String cache_weather_id = "cache_weather_id";
        String cache_weather_key = "cache_weather_all_key";
        String redisCache = redisComponent.getRedisCache(cache_weather_id, cache_weather_key);
        if (StringUtils.isNotBlank(redisCache)) {
            return JSON.parseArray(redisCache, ForeCastsResult.class);
        } else {
            List<ForeCastsResult> foreCastsResults =
                    (List<ForeCastsResult>) getWeatherInfo("all", DefinedConstant.weather_cities);
            redisComponent.putRedisCache(cache_weather_id, cache_weather_key,
                    JSON.toJSONString(foreCastsResults));
            return foreCastsResults;
        }
    }
}
